package cn.turboinfo.fuyang.api.gateway.mini.config

import cn.turboinfo.fuyang.api.gateway.mini.framework.http.context.MiniRequestContextHolder
import cn.turboinfo.fuyang.api.gateway.web.framework.http.annotation.CompanyScope
import cn.turboinfo.fuyang.api.gateway.web.framework.http.annotation.SessionScope
import cn.turboinfo.fuyang.api.gateway.web.framework.http.annotation.StaffScope
import mu.KotlinLogging
import nxcloud.ext.springmvc.automapping.spi.AutoMappingRequestParameterInjector
import nxcloud.ext.springmvc.automapping.spring.AutoMappingRequestParameterTypeBinding
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.MethodParameter
import org.springframework.web.context.request.NativeWebRequest

@Configuration
class MiniAutoMappingConfig {

    private val logger = KotlinLogging.logger {}

    /**
     * SessionScope 自动注入
     */
    @Bean
    protected fun miniSessionScopeAutoMappingRequestParameterInjector(
        autoMappingRequestParameterTypeBinding: AutoMappingRequestParameterTypeBinding,
    ): AutoMappingRequestParameterInjector {
        return object : AutoMappingRequestParameterInjector {
            override fun inject(
                parameterObj: Any,
                parameter: MethodParameter,
                resolvedParameterType: Class<*>,
                webRequest: NativeWebRequest
            ) {
                try {
                    val field = parameterObj::class.java.getDeclaredField("userId")
                    field.isAccessible = true
                    field.set(parameterObj, MiniRequestContextHolder.current().userId)
                } catch (e: NoSuchFieldException) {
                    // 忽略传递未知属性的情况
                    logger.debug { "$parameter 声明了验证 SessionScope, 但未要求 userId" }
                }

                try {
                    val field = parameterObj::class.java.getDeclaredField("userType")
                    field.isAccessible = true
                    field.set(parameterObj, MiniRequestContextHolder.current().userType)
                } catch (e: NoSuchFieldException) {
                    // 忽略传递未知属性的情况
                    logger.debug { "$parameter 声明了验证 SessionScope, 但未要求 userType" }
                }
            }

            override fun isSupported(
                parameterObj: Any,
                parameter: MethodParameter,
                resolvedParameterType: Class<*>,
                webRequest: NativeWebRequest
            ): Boolean {
                return MiniRequestContextHolder.exists() && (
                        parameter.method
                            ?.let {
                                autoMappingRequestParameterTypeBinding.getAnnotation(
                                    it,
                                    SessionScope::class.java,
                                    true
                                ) != null
                            }
                            ?: false
                        )
            }
        }
    }

    /**
     * CompanyScope 自动注入
     */
    @Bean
    protected fun miniCompanyScopeAutoMappingRequestParameterInjector(
        autoMappingRequestParameterTypeBinding: AutoMappingRequestParameterTypeBinding,
    ): AutoMappingRequestParameterInjector {
        return object : AutoMappingRequestParameterInjector {
            override fun inject(
                parameterObj: Any,
                parameter: MethodParameter,
                resolvedParameterType: Class<*>,
                webRequest: NativeWebRequest
            ) {
                try {
                    val field = parameterObj::class.java.getDeclaredField("companyId")
                    field.isAccessible = true
                    field.set(parameterObj, MiniRequestContextHolder.current().companyId)
                } catch (e: NoSuchFieldException) {
                    // 忽略传递未知属性的情况
                    logger.debug { "$parameter 声明了验证 CompanyScope, 但未要求 companyId" }
                }
            }

            override fun isSupported(
                parameterObj: Any,
                parameter: MethodParameter,
                resolvedParameterType: Class<*>,
                webRequest: NativeWebRequest
            ): Boolean {
                return MiniRequestContextHolder.exists() && (
                        parameter.method
                            ?.let {
                                autoMappingRequestParameterTypeBinding.getAnnotation(
                                    it,
                                    CompanyScope::class.java,
                                    true
                                ) != null
                            }
                            ?: false
                        )
            }
        }
    }

    /**
     * StaffScope 自动注入
     */
    @Bean
    protected fun miniStaffScopeAutoMappingRequestParameterInjector(
        autoMappingRequestParameterTypeBinding: AutoMappingRequestParameterTypeBinding,
    ): AutoMappingRequestParameterInjector {
        return object : AutoMappingRequestParameterInjector {
            override fun inject(
                parameterObj: Any,
                parameter: MethodParameter,
                resolvedParameterType: Class<*>,
                webRequest: NativeWebRequest
            ) {
                try {
                    val field = parameterObj::class.java.getDeclaredField("staffId")
                    field.isAccessible = true
                    field.set(parameterObj, MiniRequestContextHolder.current().housekeeperId)
                } catch (e: NoSuchFieldException) {
                    // 忽略传递未知属性的情况
                    logger.debug { "$parameter 声明了验证 CompanyScope, 但未要求 companyId" }
                }
            }

            override fun isSupported(
                parameterObj: Any,
                parameter: MethodParameter,
                resolvedParameterType: Class<*>,
                webRequest: NativeWebRequest
            ): Boolean {
                return MiniRequestContextHolder.exists() && (
                        parameter.method
                            ?.let {
                                autoMappingRequestParameterTypeBinding.getAnnotation(
                                    it,
                                    StaffScope::class.java,
                                    true
                                ) != null
                            }
                            ?: false
                        )
            }
        }
    }

}